/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.wsinfra.wsr.impl;

import java.math.BigInteger;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.xml.namespace.QName;

import org.apache.xmlbeans.GDuration;

import cgl.narada.wsinfra.schemas.wsr.MessageIdType;
import cgl.narada.wsinfra.schemas.wsr.NonSequenceReplyType;
import cgl.narada.wsinfra.schemas.wsr.PollRequestDocument;
import cgl.narada.wsinfra.schemas.wsr.PollRequestType;
import cgl.narada.wsinfra.schemas.wsr.RefToMessageIdsType;
import cgl.narada.wsinfra.schemas.wsr.ReplyPatternType;
import cgl.narada.wsinfra.schemas.wsr.ReplyRangeType;
import cgl.narada.wsinfra.schemas.wsr.RequestDocument;
import cgl.narada.wsinfra.schemas.wsr.RequestType;
import cgl.narada.wsinfra.schemas.wsr.ResponseDocument;
import cgl.narada.wsinfra.schemas.wsr.ResponseType;
import cgl.narada.wsinfra.schemas.wsr.SequenceNumRangeType;
import cgl.narada.wsinfra.schemas.wsr.SequenceNumType;
import cgl.narada.wsinfra.schemas.wsr.SequenceRepliesType;
import cgl.narada.wsinfra.schemas.wsr.ReplyPatternTypeValues.Enum;
import cgl.narada.wsinfra.util.XmlDateTimeConversion;
import cgl.narada.wsinfra.wsr.WsrElementCreation;

/** This is a class which facilitates the creation of WSR elements that
    are added to exchanges.

    @author Sima Patel
    $Date: 2005/05/04 18:05:34 $
    $Revision: 1.10 $
*/

public class WsrElementCreationImpl implements WsrElementCreation {
    private static final WsrElementCreation instance = 
                                                   new WsrElementCreationImpl();
    XmlDateTimeConversion xmlDateTimeConversion;
    private String moduleName = "WsrElementCreation: ";

    private WsrElementCreationImpl() {
    	xmlDateTimeConversion = XmlDateTimeConversion.getInstance();
    }
    public static WsrElementCreation getInstance() {
        return instance;
    }
    /** Creates a Request Document based on the specified mandatory parameters  
    This creates a request for PollRequest and Response RM reply pattern */
    public RequestDocument newRequest(String groupId, long sequenceNumber,
                                      boolean lastMessage,
                                      Calendar messageExpiryTime, 
                                      String replyPatternName){
        RequestDocument requestDocument = RequestDocument.Factory.newInstance();
        RequestType requestType = requestDocument.addNewRequest();
        MessageIdType messageIdType = requestType.addNewMessageId();
        messageIdType.setGroupId(groupId);
        SequenceNumType sequenceNumType = messageIdType.addNewSequenceNum();
        sequenceNumType.setLast(lastMessage);
        BigInteger bigSequenceNumber  = convertLongToBigInteger(sequenceNumber);
        sequenceNumType.setNumber(bigSequenceNumber);
        requestType.xsetExpiryTime(xmlDateTimeConversion.getXmlDateTime(
                                                           messageExpiryTime));
        ReplyPatternType replyPatternType = requestType.addNewReplyPattern();
        replyPatternType.setValue(Enum.forString(replyPatternName));
        requestType.addNewAckRequested();
        requestType.addNewDuplicateElimination();
        requestType.addNewMessageOrder();
        return requestDocument;
    }
    /** Creates a Request Document based on the specified mandatory parameters 
    This creates a request for Callback RM reply pattern */
    public RequestDocument newRequest(String groupId, long sequenceNumber,
                                      boolean lastMessage, 
                                      Calendar messageExpiryTime, 
                                      String replyPatternName, String replyTo){
        RequestDocument requestDocument = newRequest(groupId, sequenceNumber,
                                                     lastMessage, 
                                                     messageExpiryTime,
                                                     replyPatternName);
        requestDocument.getRequest().getReplyPattern().addNewReplyTo().
                                                    setReferenceScheme(replyTo);
        return requestDocument;
    }
    /** Creates a Request Document based on the specified mandatory 
    parameters with some optional parameters 
    This creates a request for PollRequest and Response RM reply pattern*/
  
    public RequestDocument newRequest(String groupId, Calendar groupExpiryTime, 
                                      long sequenceNumber,
                                      boolean lastMessage, 
                                      Calendar messageExpiryTime, 
                                      String replyPatternName){
        RequestDocument requestDocument = newRequest(groupId, sequenceNumber,
                                               lastMessage,messageExpiryTime,
                                               replyPatternName);   
        requestDocument.getRequest().getMessageId().getSequenceNum().
                                          setGroupExpiryTime(groupExpiryTime);
        return requestDocument;
   }
   
   /** Creates a Request Document based on the specified mandatory 
   parameters with some optional parameters
   This creates a request for PollRequest and Response RM reply pattern*/
  
   public RequestDocument newRequest(String groupId, 
                                     GDuration groupMaxIdleDuration,
                                     long sequenceNumber,
                                     boolean lastMessage,
                                     Calendar messageExpiryTime, 
                                     String replyPatternName){
       RequestDocument requestDocument = newRequest(groupId, sequenceNumber,
                              lastMessage, messageExpiryTime,replyPatternName);
       requestDocument.getRequest().getMessageId().getSequenceNum().
                                  setGroupMaxIdleDuration(groupMaxIdleDuration);
       return requestDocument;     
       
   }
   
   /** Creates a Request Document based on the specified mandatory parameters
   with some optional parameters
   This creates a request for callback RM reply pattern*/
   public RequestDocument newRequest(String groupId, Calendar groupExpiryTime,
                                     long sequenceNumber,
                                     boolean lastMessage, 
                                     Calendar messageExpiryTime, 
                                     String replyPatternName,
                                     String replyTo){
       RequestDocument requestDocument = newRequest(groupId, groupExpiryTime, 
                                                    sequenceNumber,
                                                    lastMessage, 
                                                    messageExpiryTime, 
                                                    replyPatternName);
       requestDocument.getRequest().getReplyPattern().addNewReplyTo().
                                                 setReferenceScheme(replyTo);
       return requestDocument;
      
   }
   /** Creates a Request Document based on the specified mandatory 
   parameters with some optional parameters
   This creates a request for callback RM reply pattern*/
   public RequestDocument newRequest(String groupId, 
                                     GDuration groupMaxIdleDuration, 
                                     long sequenceNumber,
                                     boolean lastMessage, 
                                     Calendar messageExpiryTime, 
                                     String replyPatternName,
                                     String replyTo){
       RequestDocument requestDocument = newRequest(groupId, 
                                                    groupMaxIdleDuration,
                                                    sequenceNumber, lastMessage, 
                                                    messageExpiryTime, 
                                                    replyPatternName);
       requestDocument.getRequest().getReplyPattern().addNewReplyTo().
                                                  setReferenceScheme(replyTo);
       return requestDocument;
      
   }
   /** Creates a PollRequest Document based on the specified parameters*/
   public PollRequestDocument newPollRequest(String[] groupId){
       PollRequestDocument pollRequestDocument = 
                                     PollRequestDocument.Factory.newInstance();
       PollRequestType pollRequestType = pollRequestDocument.addNewPollRequest();
       for(int i=0; i< groupId.length; i++){
           RefToMessageIdsType refToMessageIdsType = 
                                       pollRequestType.addNewRefToMessageIds();
           refToMessageIdsType.setGroupId(groupId[i]);
       } 
       return pollRequestDocument;
   }
   /** Creates a PollRequest Document based on the specified parameters*/
   public PollRequestDocument newPollRequest(String groupId){
       PollRequestDocument pollRequestDocument = 
                                          newPollRequest(new String[]{groupId});
       return pollRequestDocument;
   }
 
   /** Creates a PollRequest Document based on the specified parameters*/
   public void processPollRequest(PollRequestType pollRequestType, 
                                  String groupId, Hashtable range){
       RefToMessageIdsType refToMessageIdsType = 
                                        pollRequestType.addNewRefToMessageIds();
       refToMessageIdsType.setGroupId(groupId);
       Enumeration keys = range.keys();
       while(keys.hasMoreElements()){
           Long from = ((Long)(keys.nextElement()));
           Long to = ((Long)(range.get(from)));
           long fromRange = from.longValue();
           long toRange = to.longValue();
           BigInteger bigFrom  = convertLongToBigInteger(fromRange);
           BigInteger bigTo  = convertLongToBigInteger(toRange);
           SequenceNumRangeType sequenceNumRangeType =
                                  refToMessageIdsType.addNewSequenceNumRange();
           sequenceNumRangeType.setFrom(bigFrom);
           sequenceNumRangeType.setTo(bigTo);
       }      
   }
   /** Creates a PollRequest Document for one group which has set of reply ranges
   in Hashtable(from and to element pair)*/
   public PollRequestDocument newPollRequest(String groupId, Hashtable range){
       PollRequestDocument pollRequestDocument = 
                                      PollRequestDocument.Factory.newInstance();
       PollRequestType pollRequestType = 
                                        pollRequestDocument.addNewPollRequest();
       processPollRequest(pollRequestType, groupId, range);
       return pollRequestDocument;
   }
   /** Creates a PollRequest Document for groups which has set of reply ranges
   in Hashtable(groupId and Hashtable of replyranges)*/
   public PollRequestDocument newPollRequest(Hashtable groupIds){
       if(groupIds == null|| groupIds.size() == 0){
           return null;
       }else{
           PollRequestDocument pollRequestDocument = 
                                   PollRequestDocument.Factory.newInstance();
           PollRequestType pollRequestType = 
                                       pollRequestDocument.addNewPollRequest();
           Enumeration keys = groupIds.keys();
           while(keys.hasMoreElements()){
               String groupId = keys.nextElement().toString();
               Hashtable range = ((Hashtable)(groupIds.get(groupId)));
               processPollRequest(pollRequestType, groupId, range);
           }
           return pollRequestDocument;
       }     
   }
   /** Creates a PollRequest Document based on the specified parameters */
   public PollRequestDocument newPollRequest(String groupId, String replyTo){
       PollRequestDocument pollRequestDocument = newPollRequest(groupId);
       pollRequestDocument.getPollRequest().addNewReplyTo().setReferenceScheme
                                                                     (replyTo);
       return pollRequestDocument;
   }
   /** Creates a PollRequest Document based on the specified parameters */
   public PollRequestDocument newPollRequest(String[] groupId, String replyTo){
       PollRequestDocument pollRequestDocument = newPollRequest(groupId);
       pollRequestDocument.getPollRequest().addNewReplyTo().setReferenceScheme
                                                                      (replyTo);
       return pollRequestDocument;
   }
   /** Creates a PollRequest Document based on the specified parameters */
   public PollRequestDocument newPollRequest(String groupId, Hashtable range, 
                                             String replyTo){
       PollRequestDocument pollRequestDocument = newPollRequest(groupId,range);
       pollRequestDocument.getPollRequest().addNewReplyTo().setReferenceScheme
                                                                    (replyTo);
       return pollRequestDocument;
      
   }
   /** Creates a PollRequest Document for groups which has set of reply ranges
   in Hashtable(groupId and Hashtable of replyranges)*/
   public PollRequestDocument newPollRequest(Hashtable groupIds, String replyTo){
       PollRequestDocument pollRequestDocument = newPollRequest(groupIds);
       pollRequestDocument.getPollRequest().addNewReplyTo().setReferenceScheme
                                                                    (replyTo);
       return pollRequestDocument;
   }
  
   /** Creates a simple ResponseDocument with single Response element*/
   public ResponseDocument newResponse(){
       ResponseDocument responseDocument = ResponseDocument.Factory.newInstance();
       ResponseType responseType = responseDocument.addNewResponse();
       return responseDocument;
   }
  
   /** Adds a NonSequenceReply element(group with single message) 
   To given ResponseDocument.
   If fault is (ACK) then it is acknowledgement else it is
   fault*/
   public ResponseDocument newResponse(ResponseDocument responseDocument, 
                                                 String groupId, String fault){
       if(responseDocument == null){
           responseDocument = newResponse();
       }
       ResponseType responseType = responseDocument.getResponse();
       NonSequenceReplyType nonSequenceReplyType = 
                                         responseType.addNewNonSequenceReply();
       nonSequenceReplyType.setGroupId(groupId);
       if(!fault.equalsIgnoreCase("ACK")){
           nonSequenceReplyType.setFault(QName.valueOf(fault)); 
       } 
       return responseDocument;
       
   }
   /** Adds a SequenceReply element(group with multiple messages)To given 
   ResponseDocument
   Here Hashtable contains (Hashtable of ranges(from and to), fault or ack) pair*/
   public ResponseDocument newResponse(ResponseDocument responseDocument,
                           String groupId, Hashtable rangesAndFaultTable){
       if(responseDocument == null){
           responseDocument = newResponse();
       }
       ResponseType responseType = responseDocument.getResponse();
       SequenceRepliesType sequenceRepliesType = 
                                          responseType.addNewSequenceReplies();
       sequenceRepliesType.setGroupId(groupId);
       Enumeration keys = rangesAndFaultTable.keys();
           while(keys.hasMoreElements()){
               Hashtable ranges = ((Hashtable)(keys.nextElement()));
               String faultOrAck  = (rangesAndFaultTable.get(ranges)).toString();
               Enumeration keys1 = ranges.keys();
               long from = 0;
               long to = 0;
               while(keys1.hasMoreElements()){
                   Long fromRange = ((Long) (keys1.nextElement()));
                   from = (fromRange).longValue();
                   to= ( (Long) (ranges.get(fromRange))).longValue();
               }
               ReplyRangeType replyRangeType =
                                         sequenceRepliesType.addNewReplyRange();
               replyRangeType.setFrom(convertLongToBigInteger(from));
               replyRangeType.setTo(convertLongToBigInteger(to));
               if(!faultOrAck.equalsIgnoreCase("ACK")){
                   replyRangeType.setFault(QName.valueOf(faultOrAck));
               }
           }
      return responseDocument;
    
   }
   /** Adds a SequenceReply element(group with multiple messages)To given 
   ResponseDocument with only one ReplyRange*/
   public ResponseDocument newResponse(ResponseDocument responseDocument,
                           String groupId, long from, long to, String fault){
      
       if(responseDocument == null){
           responseDocument = newResponse();
       }
       ResponseType responseType = responseDocument.getResponse();
       
       SequenceRepliesType sequenceRepliesType = 
                                          responseType.addNewSequenceReplies();
       sequenceRepliesType.setGroupId(groupId);
       ReplyRangeType replyRangeType =sequenceRepliesType.addNewReplyRange();
       replyRangeType.setFrom(convertLongToBigInteger(from));
       replyRangeType.setTo(convertLongToBigInteger(to));
       if(!fault.equalsIgnoreCase("ACK")){
           replyRangeType.setFault(QName.valueOf(fault));
       }
       return responseDocument;
        
   }
   /** Converts a long into a BigInteger. This is the method which all such
   conversions should call. This way, if there is a more optimized way to
   do our conversions we just need to change it here and it impact every
   place. I am not very happy with this conversion, it is perhaps a little 
   inelegant */
   private BigInteger convertLongToBigInteger(long longValue) {
       String longString = ""+ longValue;
       BigInteger bigInteger = new BigInteger(longString);
       return bigInteger;
   }  
}